/* @flow */

import type VueRouter from '../index'
import {parsePath, resolvePath} from './path'
import {resolveQuery} from './query'
import {fillParams} from './params'
import {warn} from './warn'

//该方法用于将RawLocation类型的raw参数转换为Location类型的对象，并且会做一些修正工作，
//比如如果raw是对象，并且没有提供name和path属性，则会使用current的name（优先级更高）或path作为最终
//Location对象的属性
export function normalizeLocation(raw: RawLocation,
                                  current: ?Route,
                                  append: ?boolean,
                                  router: ?VueRouter): Location {
    //如果是字符串，则默认为path属性的值
    let next: Location = typeof raw === 'string' ? {path: raw} : raw
    // named target
    // name属性的优先级比path属性高
    if (next.name || next._normalized) {
        return next
    }

    // relative params
    //运行到此处，说明next的name属性不存在
    //如果if为true，则说明next的path不存在，这种情况说明不是替换url，而是替换params参数
    if (!next.path && next.params && current) {
        next = assign({}, next)
        next._normalized = true
        //用next的params参数覆盖当前current的params参数
        const params: any = assign(assign({}, current.params), next.params)

        //name属性的优先级比path高
        if (current.name) {
            next.name = current.name
            next.params = params
        } else if (current.matched.length) {
            //根据源码感觉current.matched[current.matched.length - 1]就是指向current。
            //答：不是的，current的类型为Route，而matched数组中的内部数据的类型为RouteRecord
            const rawPath = current.matched[current.matched.length - 1].path
            //将params中的数据填充到rawPath中
            next.path = fillParams(rawPath, params, `path ${current.path}`)
        } else if (process.env.NODE_ENV !== 'production') {
            warn(false, `relative params navigation requires a current route.`)
        }
        return next
    }

    //运行到这里，说明需要处理next的path属性（path属性可能不存在），但是可以发现没有处理next.params属性，
    //因为path和params不可同时存在，如果同时存在，则params属性会被忽略，详情可查看vue-router的官方文档
    //中的编程式导航中的相关说明
    const parsedPath = parsePath(next.path || '')
    //设置next的path属性时，可以设置一个相对路径，这样该路径就会相对于current.path
    const basePath = (current && current.path) || '/'
    const path = parsedPath.path
        ? resolvePath(parsedPath.path, basePath, append || next.append)
        : basePath

    const query = resolveQuery(
        parsedPath.query,
        next.query,
        router && router.options.parseQuery
    )

    //直接指定的hash属性的优先级比从path中解析出的hash字符串的优先级高
    let hash = next.hash || parsedPath.hash
    if (hash && hash.charAt(0) !== '#') {
        hash = `#${hash}`
    }

    return {
        _normalized: true,
        path,
        query,
        hash
    }
}

function assign(a, b) {
    for (const key in b) {
        a[key] = b[key]
    }
    return a
}
